package com.tnovoselec.android.common.async.notif;

import java.util.concurrent.Future;

import android.os.Handler;

import com.tnovoselec.android.common.async.listener.ResultListener;


public class NotifierFactory {

	private NotifierFactory() {
		super();
	}

	/**
	 * Creates a new {@link Notifier} which sends notifications out to
	 * {@link ResultListener}s on a single thread, regardless of the thread the
	 * notification was initiated from. The provided handler is used to post
	 * notifications on the thread the handler is running on.
	 * 
	 * <p>
	 * This notifier should be used in cases where notifications must be
	 * received on a single thread, e.g. for Android view components which have
	 * to be updated from the UI thread.
	 * </p>
	 * 
	 * @param handler 
	 *        A {@code Handler} which will be used to post notifications on one
	 *        specific thread.
	 * @return A {@link Notifier} which makes notifications on a single thread.
	 * 
	 * @throws NullPointerException if {@code handler} is null
	 */
	public static Notifier newThreadSpecificNotifier(Handler handler) {
		if (handler == null) {
			throw new NullPointerException("Handler is null!");
		}

		return new AndroidHandlerNotifier(handler);
	}

	/**
	 * Gets a reference to a {@link Notifier} which simply posts notifications
	 * directly to {@link ResultListener}s on the thread the notification was
	 * initiated from. This notifier should only be used when the listeners are
	 * thread safe and happy to receive method calls from arbitrary threads.
	 * <p>
	 * The notify method is simply implemented as:
	 * </p>
	 * <pre>
	 * public &lt;K, T&gt; void notify(ResultListener&lt;K, T&gt; listener, K key, Future&lt;T&gt; result) {
	 *     listener.onResult(key, result);
	 * }
	 * </pre>
	 * 
	 * @return A reference to the standard notifier.
	 */
	public static Notifier getBasicNotifier() {
		return NotifierFactory.BASIC_NOTIFIER;
	}

	private static final Notifier BASIC_NOTIFIER = new Notifier() {

		@Override
		public <K, T> void notify(ResultListener<K, T> listener, K key, Future<T> result) {
			if (listener == null) {
				throw new NullPointerException("Listener is null!");
			} else if (result == null) {
				throw new NullPointerException("Result is null!");
			} else if (!result.isDone()) {
				throw new IllegalStateException("Result has not finished calculating!");
			}

			listener.onResult(key, result);
		}
	};
}
